package com.cardone.persistent.support;

import com.cardone.common.dto.PaginationDto;
import com.cardone.common.template.support.TemplateSupport;
import com.cardone.common.util.PaginationUtils;
import com.cardone.persistent.builder.Model;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * jdbc dao
 *
 * @author yaohaitao
 */
@Getter
@Setter
@Slf4j
@Accessors(chain = true)
public class NamedParameterJdbcTemplateSupport implements JdbcTemplateSupport {
    /**
     * namedParameterJdbcTemplateRouterSupport
     */
    @lombok.Setter
    @lombok.Getter
    private org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    /**
     * 模板支持
     */
    private TemplateSupport templateSupport;

    @Override
    public int[] batchUpdate(String id, Map<String, Object> model, List<Map<String, Object>> batchValueList) {
        if (CollectionUtils.isEmpty(batchValueList)) {
            return new int[0];
        }

        final String sql = this.builderSql(id, model);

        final SqlParameterSource[] batchArgs = new SqlParameterSource[batchValueList.size()];

        for (int i = 0; i < batchValueList.size(); i++) {
            batchArgs[i] = new MapSqlParameterSource(batchValueList.get(i));
        }

        return this.namedParameterJdbcTemplate.batchUpdate(sql, batchArgs);
    }

    @Override
    @org.springframework.transaction.annotation.Transactional
    public int[] batchUpdate(final String id, final List<Map<String, Object>> batchValueList) {
        if (CollectionUtils.isEmpty(batchValueList)) {
            return new int[0];
        }

        return this.batchUpdate(id, batchValueList.get(0), batchValueList);
    }

    @Override
    public String builderSql(final String id, final Object model) {
        final String sql = this.templateSupport.process(id, model);

        return sql;
    }

    @Override
    public <R> R find(final Class<R> mappedClass, final String id, final Map<String, Object> findMap) {
        final List<R> rList = this.findList(mappedClass, id, findMap);

        if (CollectionUtils.isEmpty(rList)) {
            return null;
        }

        if (NamedParameterJdbcTemplateSupport.log.isErrorEnabled() && (rList.size() > 1)) {
            NamedParameterJdbcTemplateSupport.log.error("单条数据查询方法，查询出" + rList.size() + "条数据:id=" + id + ",model=" + findMap);
        }

        return rList.get(0);
    }

    @Override
    public Map<String, Object> find(final String id, final Map<String, Object> findMap) {
        final List<Map<String, Object>> mapList = this.findList(id, findMap);

        if (CollectionUtils.isEmpty(mapList)) {
            return null;
        }

        if (NamedParameterJdbcTemplateSupport.log.isErrorEnabled() && (mapList.size() > 1)) {
            NamedParameterJdbcTemplateSupport.log.error("单条数据查询方法，查询出" + mapList.size() + "条数据:id=" + id + ",model=" + findMap);
        }

        return mapList.get(0);
    }

    @Override
    public <P> List<P> findList(final Class<P> mappedClass, final String id) {
        return this.findList(mappedClass, id, null);
    }

    @Override
    public <R> List<R> findList(final Class<R> mappedClass, final String id, final Map<String, Object> findListMap) {
        final String sql = this.builderSql(id, findListMap);

        return this.namedParameterJdbcTemplate.query(sql, findListMap, new org.springframework.jdbc.core.BeanPropertyRowMapper(mappedClass));
    }

    @Override
    public List<Map<String, Object>> findList(final String id) {
        return this.findList(id, null);
    }

    @Override
    public List<Map<String, Object>> findList(final String id, final Map<String, Object> findListMap) {
        final String sql = this.builderSql(id, findListMap);

        return this.namedParameterJdbcTemplate.queryForList(sql, findListMap);
    }

    @Override
    public <R> PaginationDto<R> pagination(final Class<R> mappedClass, final PaginationArgs paginationArgs, final Map<String, Object> paginationMap) {
        final Map<String, Object> newPaginationMap = Maps.newHashMap(paginationMap);

        final long totalSize = this.read(Long.class, paginationArgs.getReadId(), newPaginationMap);

        final PaginationDto<R> pagination = PaginationUtils.newPagination(paginationArgs.getNo(), paginationArgs.getSize(), 6, totalSize);

        if ((totalSize < 1) || (totalSize < pagination.getBeginRowNum())) {
            return pagination.setDataList(Lists.<R>newArrayList());
        }

        final String beginKey = newPaginationMap.containsKey(Model.Keys.beginRowNum.stringValue()) ? Model.Keys.paginationBeginRowNum.stringValue() : Model.Keys.beginRowNum.stringValue();

        newPaginationMap.put(beginKey, pagination.getBeginRowNum());

        final String endKey = newPaginationMap.containsKey(Model.Keys.endRowNum.stringValue()) ? Model.Keys.paginationEndRowNum.stringValue() : Model.Keys.endRowNum.stringValue();

        newPaginationMap.put(endKey, pagination.getEndRowNum());

        final String sizeKey = newPaginationMap.containsKey(Model.Keys.size.stringValue()) ? Model.Keys.paginationSize.stringValue() : Model.Keys.size.stringValue();

        newPaginationMap.put(sizeKey, pagination.getSize());

        final List<R> dataList = this.findList(mappedClass, paginationArgs.getFindId(), newPaginationMap);

        pagination.setDataList(dataList);

        return pagination;
    }

    @Override
    public PaginationDto<Map<String, Object>> pagination(final PaginationArgs paginationArgs, final Map<String, Object> paginationMap) {
        final Map<String, Object> newPaginationMap = Maps.newHashMap(paginationMap);

        final long totalSize = this.read(Long.class, paginationArgs.getReadId(), null, newPaginationMap);

        final PaginationDto<Map<String, Object>> pagination = PaginationUtils.newPagination(paginationArgs.getNo(), paginationArgs.getSize(), 6, totalSize);

        if ((totalSize < 1) || (totalSize < pagination.getBeginRowNum())) {
            return pagination.setDataList(Lists.<Map<String, Object>>newArrayList());
        }

        final String beginKey = newPaginationMap.containsKey(Model.Keys.beginRowNum.stringValue()) ? Model.Keys.paginationBeginRowNum.stringValue() : Model.Keys.beginRowNum.stringValue();

        newPaginationMap.put(beginKey, pagination.getBeginRowNum());

        final String endKey = newPaginationMap.containsKey(Model.Keys.endRowNum.stringValue()) ? Model.Keys.paginationEndRowNum.stringValue() : Model.Keys.endRowNum.stringValue();

        newPaginationMap.put(endKey, pagination.getEndRowNum());

        final String sizeKey = newPaginationMap.containsKey(Model.Keys.size.stringValue()) ? Model.Keys.paginationSize.stringValue() : Model.Keys.size.stringValue();

        newPaginationMap.put(sizeKey, pagination.getSize());

        final List<Map<String, Object>> dataList = this.findList(paginationArgs.getFindId(), newPaginationMap);

        pagination.setDataList(dataList);

        return pagination;
    }

    @Override
    public <R> R read(final Class<R> requiredType, final String id) {
        return this.read(requiredType, id, new HashMap<>());
    }

    @Override
    public <R> R read(final Class<R> requiredType, final String id, final Map<String, Object> readMap) {
        final List<R> rList = this.readList(requiredType, id, readMap);

        if (org.apache.commons.collections.CollectionUtils.isEmpty(rList)) {
            return null;
        }

        if (NamedParameterJdbcTemplateSupport.log.isErrorEnabled() && (rList.size() > 1)) {
            NamedParameterJdbcTemplateSupport.log.error("单个值查询方法，查询出" + rList.size() + "个值:id=" + id + ",model=" + readMap);
        }

        return rList.get(0);
    }

    @Override
    public <R> R read(final Class<R> requiredType, final String id, final String objectId) {
        return this.read(requiredType, id, objectId, new HashMap<>());
    }

    @Override
    public <R> R read(final Class<R> requiredType, final String id, final String objectId, final Map<String, Object> readMap) {
        final Map<String, Object> newReadMap = Maps.newHashMap(readMap);

        if (StringUtils.isNotBlank(objectId)) {
            newReadMap.put(Model.Keys.objectId.stringValue(), objectId);
        }

        return this.read(requiredType, id, newReadMap);
    }

    @Override
    public <R> List<R> readList(final Class<R> elementType, final String id) {
        return this.readList(elementType, id, Model.Keys.objectId.stringValue(), new HashMap<>());
    }

    @Override
    public <R> List<R> readList(final Class<R> elementType, final String id, final Map<String, Object> readListMap) {
        final String sql = this.builderSql(id, readListMap);

        return this.namedParameterJdbcTemplate.queryForList(sql, readListMap, elementType);
    }

    @Override
    public <R> List<R> readList(final Class<R> elementType, final String id, final String objectId) {
        return this.readList(elementType, id, objectId, new HashMap<>());
    }

    @Override
    public <R> List<R> readList(final Class<R> elementType, final String id, final String objectId, final Map<String, Object> readListMap) {
        final Map<String, Object> newReadListMap = Maps.newHashMap(readListMap);

        if (StringUtils.isNotBlank(objectId)) {
            newReadListMap.put(Model.Keys.objectId.stringValue(), objectId);
        }

        return this.readList(elementType, id, newReadListMap);
    }

    @Override
    @org.springframework.transaction.annotation.Transactional
    public int[] update(final List<String> idList, final Map<String, Object> updateMap) {
        if (org.apache.commons.collections.CollectionUtils.isEmpty(idList)) {
            return ArrayUtils.EMPTY_INT_ARRAY;
        }

        int[] updateCounts = new int[idList.size()];

        for (int i = 0, k = idList.size(); i < k; i++) {
            updateCounts[i] = this.update(idList.get(i), updateMap);
        }

        return updateCounts;
    }

    @Override
    @org.springframework.transaction.annotation.Transactional
    public int update(final String id) {
        final String sql = this.builderSql(id, null);

        return this.namedParameterJdbcTemplate.getJdbcOperations().update(sql);
    }

    @Override
    @org.springframework.transaction.annotation.Transactional
    public int[] update(final String id, final List<Map<String, Object>> updateMapList) {
        if (org.apache.commons.collections.CollectionUtils.isEmpty(updateMapList)) {
            return ArrayUtils.EMPTY_INT_ARRAY;
        }

        int[] updateCounts = new int[updateMapList.size()];

        for (int i = 0, k = updateMapList.size(); i < k; i++) {
            updateCounts[i] = this.update(id, updateMapList.get(i));
        }

        return updateCounts;
    }

    @Override
    @org.springframework.transaction.annotation.Transactional
    public int update(final String id, final Map<String, Object> updateMap) {
        final String sql = this.builderSql(id, updateMap);

        return this.namedParameterJdbcTemplate.update(sql, updateMap);
    }
}
